import Model.*;
import org.codehaus.jackson.map.ObjectMapper;

import javax.servlet.http.HttpSession;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Created by zl206 on 2016/11/6.
 */
@ServerEndpoint("/snake")
public class snakeServers {
    private static Map<String,snake> map=new ConcurrentHashMap<String, snake>();
    private static bg bg=new bg(4800,3600);
    private static ConcurrentLinkedQueue<bgPoint> bgPoint=Tool.getBgPoints(500,bg);
    private static ConcurrentLinkedQueue<bgPoint> BBbgPoint=Tool.getBgPoints(0,bg);
    private  ObjectMapper objectMapper=new ObjectMapper();
    private  long lastTime=new Date().getTime();
    private  long deltaTime=0;
    private Session session;
    private snake snake;
    @OnOpen
    public void open(Session session) throws IOException {
        HttpSession user = (HttpSession) session.getUserProperties().get("user");
        this.session=session;
        snake = new snake(bg, session.getId());
        String s = objectMapper.writeValueAsString(snake);
        session.getBasicRemote().sendText("{\"code\":\"you\",\"snake\":"+s+",\"dd\":{},\"bgdd\":{}}");
        System.out.println("有人连接");
        lastTime=new Date().getTime();
    }
    @OnMessage
    public void message(String msg) throws CloneNotSupportedException {
        if(!map.containsKey(session.getId())){
            System.out.println("put");
            map.put(session.getId(), snake);
        }
        Msg msg1 = null;
        try {
            msg1 = objectMapper.readValue(msg, Msg.class);
        } catch (IOException e) {
            e.printStackTrace();
        }
        long now=new Date().getTime();
        deltaTime=now-lastTime;
        lastTime=now;
        try {
            gameRun(msg1);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    @OnClose
    public void cls() throws IOException {
        map.remove(session.getId());
        System.out.println("有人离开");
    }

    @OnError
    public void error(Throwable error) throws IOException {
        error.printStackTrace();
        if (map.containsKey(session.getId()))
            map.remove(session.getId());
        System.out.println("有人离开");
    }


    public void gameRun(Msg msg) throws IOException {
        move(msg);
        eat(bgPoint,1);
        eat(BBbgPoint,10);
        fight();
        send();
    }

    public void move(Msg msg) throws IOException {
        double angle = msg.getAngle();
        boolean speedup = msg.getSpeedup();
        snake.setAngle(angle);
        snake.lostLoseTime((int) -deltaTime);
        if (snake.getLoseTime()>16){
            if(speedup&&snake.getPoints().size()>30&&snake.getLoseSpeed()<=0){
                snake.setLoseSpeed(100);
                snake.dropLength();
            }
            while (snake.getLoseTime()>16){
                point forward = Tool.forward(snake.getHead(), 3, angle);
                snake.setHead(forward);
                if (!snake.dropScore()){
                    snake.dropLength();
                }
                if(snake.isOut(bg.getWidth(),bg.getHeight())){
                    restart();
                    return;
                }
                if(snake.getLoseSpeed()>0&&speedup){
                    snake.lostLoseTime(6);
                    snake.lostLoseSpeed(1);
                }else {
                    snake.lostLoseTime(16);
                }
            }
        }
    }

    public void eat(ConcurrentLinkedQueue<bgPoint> bgPoint,int score){
        point point=snake.getHead();
        double headSize= snake.getHeadSize();
        bgPoint.forEach(e->{
            double l = Tool.getL(e.getPoint(), point);
            if(l<headSize){
                bgPoint.remove(e);
                if (score==1){
                    bgPoint.add(new bgPoint(bg));
                }
                snake.addScore(10*score);
            }else if(l<2*headSize){
                e.setPoint(Tool.forward(e.getPoint(),point,deltaTime/3));
            }
        });
    }

    public void fight(){
        point point = snake.getHead();
        double headSize1 = snake.getHeadSize();
        AtomicInteger i=new AtomicInteger(0);
        map.entrySet().forEach(e->{
            if(!e.getValue().getId().equals(snake.getId())){
                double headSize = e.getValue().getHeadSize();
                final int L= (int) (headSize/4);
                ConcurrentLinkedQueue<point> points = e.getValue().getPoints();
                points.forEach(e1->{
                    if(i.getAndIncrement()%L==0&&Tool.isClose(point,e1,headSize+headSize1)){
                        restart();
                    }
                });
            }
        });
    }

    public void restart(){
        ConcurrentLinkedQueue<point> points= snake.getPoints();
        AtomicInteger i=new AtomicInteger(0);
        points.forEach((e)->{
            if (i.getAndIncrement()%10==0){
                bgPoint bgPoint = new bgPoint();
                bgPoint.setPoint(e);
                bgPoint.setString(Tool.color());
                BBbgPoint.add(bgPoint);
            }
        });
        snake.reset();
    }


    private void send() throws IOException {
            String s = objectMapper.writeValueAsString(map);
            String s1 = objectMapper.writeValueAsString(bgPoint);
            String s2 = objectMapper.writeValueAsString(BBbgPoint);
            session.getBasicRemote().sendText("{\"code\":\"data\",\"snake\":"+s+",\"dd\":"+s1+",\"bgdd\":"+s2+"}");
    }



}
